iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
Modern Web

Go,一起成為全端吧!—— 給前端工程師的 Golang 後端學習筆記系列 第 6

Day6 - Go 的錯誤處理:error、panic 與 recover

  • 分享至 

  • xImage
  •  

今天要來介紹的是 Go 的錯誤處理。

為什麼要有錯誤處理呢?原因是我們在執行程式的時候,會寫一些條件判斷,而當這些條件不符合的時候,就會需要告訴使用者或程式撰寫者,現在有錯誤出現喔!要不要檢查一下輸入的參數或是資料~

而這樣的設計,可以讓程式運行時,避免系統 crash 或發生一些無法挽回的悲劇。
所以是非常重要的呢!

Go 有兩種錯誤處理的方式:

  1. 一般錯誤(error)
  2. 嚴重錯誤(panic、recover)

一般錯誤(error)

error 是最簡單的錯誤處理方式,一般常用 if err != nil 來檢查是否有錯誤。
這部分很常用在判斷函式執行時是否有回傳錯誤,如果有,就會把錯誤印出來。我來看看以下範例:

  1. 讀檔案
package main
import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Open("test.txt")        // 回傳檔案和錯誤
	
	if err != nil {                         // 判斷是否有回傳錯誤
		fmt.Println("檔案開啟失敗:", err)
		return                                // 遇到錯誤就結束程式或函式
	}
	defer file.Close()

	fmt.Println("成功:", file.Name())
}
  1. 型別轉換
package main
import (
	"fmt"
	"strconv"
)

func main() {
	numStr := "123a"
	num, err := strconv.Atoi(numStr)  // 轉成整數
	
	if err != nil {                   // 判斷是否有回傳錯誤
		fmt.Println("失敗:", err)
		return
	}
	fmt.Println("成功:", num)
}

輸出:

失敗: strconv.Atoi: parsing "123a": invalid syntax

以上就是常見的錯誤處理方式, 跟其他語言使用 trycatch 比較不一樣~


嚴重錯誤(panic、recover)

這邊指的嚴重錯誤是 panic ,它會搭配 revocer() 一起做使用,避免程式 crash 掉。而且我們不需要主動呼叫 panic ,只要程式上做了非法操作,Go 自己會幫你呼叫 😆

那什麼情況下會導致 panic 呢? 例如:除以 0、記憶體壞了、初始化失敗等。
👉 在發生「 真的無法處理的錯誤 」的情況下,才會出現。

我們來看範例:

package main
import "fmt"

func divide(a, b int) int {
	if b == 0 {
		panic("division by zero!")    // 除以 0 導致 panic
	}
	return a / b
}

func main() {
	fmt.Println("開始")
	result := divide(10, 0)
	fmt.Println("結果:", result)    // 不會執行這行程式
}

輸出:

開始
panic: division by zero!

goroutine 1 [running]:
main.divide(...)
        /Users/XXXX/Desktop/To-do-List/app/main.go:7
main.main()
        /Users/XXXX/Desktop/To-do-List/app/main.go:14 +0x5a
exit status 2

可以看到上面的範例,因為發生 panic,程式就直接中斷,沒有執行 fmt.Println("結果:", result) 這一行。

所以,為了讓程式可以繼續執行,我們必須加上 recover(),才能讓程式完整的執行,避免 crash。
修改後範例:

package main
import "fmt"

func divide(a, b int) (result int) {
	defer func() {                       // defer 會搭配 recover() 來捕捉 panic 錯誤訊息
		if r := recover(); r != nil {    // 判斷是否有錯誤
			fmt.Println("panic:", r)
			result = 0                            
		}
	}()
	return a / b
}

func main() {
	fmt.Println("開始")

	fmt.Println("10 / 2 =", divide(10, 2))
	fmt.Println("10 / 0 =", divide(10, 0))

	fmt.Println("繼續執行,不會崩潰")
}

輸出:

開始
10 / 2 = 5
panic: runtime error: integer divide by zero
10 / 0 = 0
繼續執行,不會崩潰

在上面的範例程式中,可以看到它多加了 recover()defer 來防止程式中斷的問題。
這兩個一定會同時出現,所以使用 recover() 時,一定要放在 defer 函式裡使用,否則會接不到錯誤。
recover() 適用在伺服器、goroutine,避免因為一個錯誤而導致整個服務或系統掛掉~
算是 Go 的保險機制 😆😆😆

那我們來看一下 defer 的作用:
從範例可以看到 defer func() {……}() 這個函式,它會等外層的程式執行完之後,在 return 回傳前執行。以上面的例子來看,在執行 a / b 的算式時,系統發現這個計算有問題,所以才執行 defer 裡面的程式,並返回錯誤訊息。

這個結構看起來是不是很眼熟:

if r := recover(); r != nil {         
	fmt.Println("panic:", r)
	result = 0                            
}

沒錯!我們在介紹基本語法條件時,有它! 這個寫法是 if 帶初始化語句的用法,算是 Go 接收錯誤時的固定用法~ 大家可以記一下!

總結一下今天介紹的內容:
在大多數的情況下,還是使用一般的錯誤處理方式(error,if err != nil),返回錯誤訊息。
非必要時才會使用 panicrecover


上一篇
Day5 - Go 的模組化:拆分程式結構
下一篇
Day7 - Go test:單元測試入門
系列文
Go,一起成為全端吧!—— 給前端工程師的 Golang 後端學習筆記12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言